package main;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class AllCombine {

	String termFormatted2;
	
	String termFormatted1;

	String termFormatted;

	String topPassages;

	String[] phases;

	int outputLine = 1000;

	String outputFile;

	List topics;
	
	List topics1;

	List topics2;

	List topics3 = new ArrayList<Topic>();

	final static int totalSize = 1000;

	/**
	 * 1-100 reranking+100-200 reranking + 200-500 reranking + 500-1000 reranking
	 *
	 */
	public AllCombine() {

		initConfig();

		read();
		System.out.println("sort begin.............");
		Date begin = new Date();
		for (Iterator iter = topics.iterator(); iter.hasNext();) {
			Topic topic = (Topic) iter.next();
			Topic topic1 = getTopic1(topic.getId());
			Topic topic2 = getTopic2(topic.getId());
			topics3.add(combine(topic, topic1, topic2));
		}

		System.out.println("sort end.............");
		Date end = new Date();
		write(topics3);
		System.out.println("sort time:" + (end.getTime() - begin.getTime()));

	}
	
	private void filtSamePassage(List psgs) {

		List removeIndex = new ArrayList();
		for (int i = 1; i < psgs.size(); i++) {
			Passage psg1 = (Passage) psgs.get(0);
			Passage psg2 = (Passage) psgs.get(i);

			if (psg1.getDocumentID().equals(psg2.getDocumentID())) {
				removeIndex.add(new Integer(i));
			}
		}
		// remove psgs
		if (removeIndex.size() > 0) {
			for (int i = psgs.size(); i >= 0; i--) {
				for (int j = 0; j < removeIndex.size(); j++) {
					if (i == ((Integer) removeIndex.get(j)).intValue()) {
						psgs.remove(i);
					}
				}
			}
			filtSamePassage(psgs);
		}
	}

	private void write(List topics) {
		try {
			BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile));
			for (Iterator iter = topics.iterator(); iter.hasNext();) {
				Topic topic = (Topic) iter.next();
				int i = 0;
				for (Iterator itP = topic.getSortedPassages().iterator(); itP.hasNext() && i < outputLine; i++) {
					Passage psg = (Passage) itP.next();
					bw.write(psg.toString(i + 1) + "\r\n");
					System.out.println(psg);
				}
			}
			bw.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private Topic combine(Topic topic1, Topic topic2, Topic topic3) {
		List<Passage> psgs1 = topic1.getPassages();
		List<Passage> psgs2 = topic2.getPassages();
		List<Passage> psgs3 = topic3.getPassages();
		Topic topic = new Topic();
//		for (Iterator<Passage> iter1 = psgs1.iterator(); iter1.hasNext();) {
//			Passage psg1 = iter1.next();
//			for (Iterator<Passage> iter2 = psgs2.iterator(); iter2.hasNext();) {
//				Passage psg2 = iter2.next();
//				if (psg1.getDocumentID().equals(psg2.getDocumentID())) {
//					topic3.addPassage(merge(psg1, psg2));
//					iter1.remove();
//					iter2.remove();
//					break;
//				}
//			}
//		}
		topic.getPassages().addAll(psgs1);
		topic.getPassages().addAll(psgs2);
		topic.getPassages().addAll(psgs3);
		Collections.sort(topic.getPassages(),new PassageComp());
		//filtSamePassage(topic3.getSortedPassages());
		topic.setSortedPassages(topic.getPassages().subList(0, 1000));
		//filtSamePassage(topic3.getSortedPassages());
		return topic;
	}

	//This method is to merge the passages with same documentIDs. 
	//There are two main types of conditions between two passages. One is that they are independent; the other is they are intercross each other.
	private Passage merge(Passage psg1, Passage psg2) {
		Passage psg = new Passage();
		if (psg1.getStart() >= psg2.getStart()
				&& (psg1.getStart() + psg1.getOffset()) <= (psg2.getStart() + psg2.getOffset())) {
			psg.setDocumentID(psg2.getDocumentID());
			psg.setStart(psg2.getStart());
			psg.setOffset(psg2.getOffset());
			psg.setWeight(psg1.getWeight().add(psg2.getWeight()));
		} else if (psg1.getStart() <= psg2.getStart()
		&& ((psg1.getStart() + psg1.getOffset()) >= (psg2.getStart() + psg2.getOffset()))) {
			psg.setDocumentID(psg1.getDocumentID());
			psg.setStart(psg1.getStart());
			psg.setOffset(psg1.getOffset());
			psg.setWeight(psg1.getWeight().add(psg2.getWeight()).divide(new BigDecimal(2)));
			//psg.setWeight(psg1.getWeight().add(psg2.getWeight()).divide(new BigDecimal(2)));
		} else if (psg2.getStart() <= (psg1.getStart() + psg1.getOffset()) && ((psg1.getStart() + psg1.getOffset()) <= (psg2.getStart() + psg2.getOffset())) ){
			psg.setDocumentID(psg1.getDocumentID());
			psg.setStart(psg1.getStart());
			psg.setOffset(psg2.getStart()+psg2.getOffset()-psg1.getStart());
			psg.setWeight(psg1.getWeight().add(psg2.getWeight()).divide(new BigDecimal(2)));
		}else if (psg1.getStart() <= (psg2.getStart() + psg2.getOffset()) && ((psg2.getStart() + psg2.getOffset()) <= (psg1.getStart() + psg1.getOffset())) ){
			psg.setDocumentID(psg1.getDocumentID());
			psg.setStart(psg2.getStart());
			psg.setOffset(psg1.getStart()+psg1.getOffset()-psg2.getStart());
			psg.setWeight(psg1.getWeight().add(psg2.getWeight()).divide(new BigDecimal(2)));
		}
		else if ((psg1.getStart() + psg1.getOffset()) < psg2.getStart() || ((psg2.getStart() + psg2.getOffset()) < psg1.getStart()) ) {
			if (psg1.getWeight().compareTo(psg2.getWeight())<0) {
				psg.setDocumentID(psg2.getDocumentID());
				psg.setStart(psg2.getStart());
				psg.setOffset(psg2.getOffset());
				psg.setWeight(psg2.getWeight());
			} 
			else {
				psg.setDocumentID(psg1.getDocumentID());
				psg.setStart(psg1.getStart());
				psg.setOffset(psg1.getOffset());
				psg.setWeight(psg1.getWeight());
			}
		} 
		else {
			psg.setDocumentID(psg1.getDocumentID());
			psg.setStart(psg1.getStart());
			psg.setOffset(psg1.getOffset());
			psg.setWeight(psg1.getWeight());
		}
		psg.setTerms(psg1.getTerms());
		psg.setTopicID(psg1.getTopicID());
		return psg;
	}

/*	private List[] split(List psgs) {
		List[] lists = new List[phases.length];
		for (int i = 0; i < phases.length; i++) {
			if(i==0){
				lists[i] = new ArrayList();
				lists[i].addAll(psgs.subList(0 , Integer.valueOf(phases[i]).intValue()));
			}else{
				lists[i] = new ArrayList();
				lists[i].addAll(psgs.subList(Integer.valueOf(phases[i - 1]).intValue(), Integer.valueOf(phases[i]).intValue()));
			}
		}
		return lists;
	}

	private List merge(List[] lists) {
		List list = new ArrayList();
		for (int i = 0; i < lists.length; i++) {
			list.addAll(lists[i]);
		}
		return list;
	}

	private List cluster(List psgs, List clusters) {
		Passage firstPsg = (Passage) psgs.get(0);
		List clusterX = new ArrayList();
		for (Iterator itp = psgs.iterator(); itp.hasNext();) {
			Passage psg = (Passage) itp.next();
			BigDecimal simRank = calSimRank(firstPsg, psg);
			psg.setSimilarityRank(simRank);
			if (simRank.intValue() == 1) {
				clusterX.add(psg);
//				itp.remove();
			}
		}
		clusters.add(clusterX);
		psgs.removeAll(clusterX);
		if (psgs.size() > 0) {
			cluster(psgs, clusters);
		}
		return clusters;
	}

	private List resort(List clusters) {
		int clusterSize = 0;
		int clusterNum = clusters.size();
		for (int i = 0; i < clusterNum; i++) {
			List clusterX = (List) clusters.get(i);
			if (clusterX.size() > clusterSize) {
				clusterSize = clusterX.size();
			}
		}

		List newlist = new ArrayList();
		// init list
		for (int i = 0; i < clusterSize * clusterNum; i++) {
			newlist.add(null);
		}

		// set psg to new list
		for (int i = 0; i < clusterNum; i++) {
			List clusterX = (List) clusters.get(i);
			for (int j = 0; j < clusterX.size(); j++) {
				newlist.set(clusterNum * j + i, clusterX.get(j));
			}
		}

		// remove null
		for (int i = newlist.size() - 1; i >= 0; i--) {
			if (newlist.get(i) == null) {
				newlist.remove(i);
			}
		}

		return newlist;
	}

	private void filtSamePassage(List psgs) {

		List removeIndex = new ArrayList();
		for (int i = 1; i < psgs.size(); i++) {
			Passage psg1 = (Passage) psgs.get(0);
			Passage psg2 = (Passage) psgs.get(i);

			if (psg1.getDocumentID().equals(psg2.getDocumentID())) {
				removeIndex.add(new Integer(i));
			}
		}
		// remove psgs
		if (removeIndex.size() > 0) {
			for (int i = psgs.size(); i >= 0; i--) {
				for (int j = 0; j < removeIndex.size(); j++) {
					if (i == ((Integer) removeIndex.get(j)).intValue()) {
						psgs.remove(i);
					}
				}
			}
			filtSamePassage(psgs);
		}
	}

	*//**
	 * @param firstPsg
	 * @param psg
	 * @return
	 *//*
	private BigDecimal calSimRank(Passage firstPsg, Passage psg) {
		String[] firstTerms = firstPsg.getFoundTerms();
		String[] theTerms = psg.getFoundTerms();
		int totalNum = firstTerms.length;
		if (theTerms.length > firstTerms.length) {
			totalNum = theTerms.length;
		}
		int sameNum = 0;
		for (int i = 0; i < firstTerms.length; i++) {
			for (int j = 0; j < theTerms.length; j++) {
				if (firstTerms[i].equals(theTerms[j])) {
					sameNum++;
				}
			}
		}
		BigDecimal simRank = new BigDecimal(sameNum).divide(new BigDecimal(totalNum), 4, BigDecimal.ROUND_HALF_EVEN);
		return simRank;
	}*/

	//Read the files.
	//The files are really read by the methods of 
	private void read() {
		topics = new ArrayList();
		topics1 = new ArrayList();
		topics2 = new ArrayList();
		System.out.println("read file begin.............");
		try {

			// readPassages();
			readTermFormatted();
			
			readTermFormatted1();

			readTermFormatted2();

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("read file end.............");

	}

	/**
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	private void readTermFormatted() throws FileNotFoundException, IOException {
		BufferedReader fmt_file = new BufferedReader(new FileReader(termFormatted));
		for (String tmp = fmt_file.readLine(); tmp != null; tmp = fmt_file.readLine()) {
			String[] tmpArr = tmp.split(" ");
			if (tmpArr.length == 7) {
				Topic topic = getTopic(tmpArr[0]);
				if (topic == null) {
					topic = new Topic();
					topic.setId(tmpArr[0]);
					topics.add(topic);
				}

				Passage psg = new Passage();
				psg.setTopicID(tmpArr[0]);
				psg.setDocumentID(tmpArr[1]);
				psg.setPassageID(tmpArr[2]);
				psg.setWeight(new BigDecimal(tmpArr[3]));
				psg.setStart(new Integer(tmpArr[4]));
				psg.setOffset(new Integer(tmpArr[5]));
				psg.setTerms(tmpArr[6]);
				psg.setOutString(tmp);
				topic.addPassage(psg);
			} else {
				System.err.println("file format error!");
			}
		}
		fmt_file.close();
	}
	
	private void readTermFormatted1() throws FileNotFoundException, IOException {
		BufferedReader fmt_file = new BufferedReader(new FileReader(termFormatted1));
		for (String tmp = fmt_file.readLine(); tmp != null; tmp = fmt_file.readLine()) {
			String[] tmpArr = tmp.split(" ");
			if (tmpArr.length == 7) {
				Topic topic = getTopic(tmpArr[0]);
				if (topic == null) {
					topic = new Topic();
					topic.setId(tmpArr[0]);
					topics1.add(topic);
				}

				Passage psg = new Passage();
				psg.setTopicID(tmpArr[0]);
				psg.setDocumentID(tmpArr[1]);
				psg.setPassageID(tmpArr[2]);
				psg.setWeight(new BigDecimal(tmpArr[3]));
				psg.setStart(new Integer(tmpArr[4]));
				psg.setOffset(new Integer(tmpArr[5]));
				psg.setTerms(tmpArr[6]);
				psg.setOutString(tmp);
				topic.addPassage(psg);
			} else {
				System.err.println("file format error!");
			}
		}
		fmt_file.close();
	}

	private void readTermFormatted2() throws FileNotFoundException, IOException {
		BufferedReader fmt_file = new BufferedReader(new FileReader(termFormatted2));
		for (String tmp = fmt_file.readLine(); tmp != null; tmp = fmt_file.readLine()) {
			String[] tmpArr = tmp.split(" ");
			if (tmpArr.length == 7) {
				Topic topic = getTopic2(tmpArr[0]);
				if (topic == null) {
					topic = new Topic();
					topic.setId(tmpArr[0]);
					topics2.add(topic);
				}

				Passage psg = new Passage();
				psg.setTopicID(tmpArr[0]);
				psg.setDocumentID(tmpArr[1]);
				psg.setPassageID(tmpArr[2]);
				psg.setWeight(new BigDecimal(tmpArr[3]));
				psg.setStart(new Integer(tmpArr[4]));
				psg.setOffset(new Integer(tmpArr[5]));
				psg.setTerms(tmpArr[6]);
				psg.setOutString(tmp);
				topic.addPassage(psg);
			} else {
				System.err.println("file format error!");
			}
		}
		fmt_file.close();
	}

	/**
	 * @param id
	 * @return
	 */
	private Topic getTopic(String id) {
		for (Iterator iter = topics.iterator(); iter.hasNext();) {
			Topic thetopic = (Topic) iter.next();
			if (id.equals(thetopic.getId())) {
				return thetopic;
			}
		}
		return null;
	}
	
	private Topic getTopic1(String id) {
		for (Iterator iter = topics1.iterator(); iter.hasNext();) {
			Topic thetopic = (Topic) iter.next();
			if (id.equals(thetopic.getId())) {
				return thetopic;
			}
		}
		return null;
	}


	private Topic getTopic2(String id) {
		for (Iterator iter = topics2.iterator(); iter.hasNext();) {
			Topic thetopic = (Topic) iter.next();
			if (id.equals(thetopic.getId())) {
				return thetopic;
			}
		}
		return null;
	}

	/**
	 * @return
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
/*	private BufferedReader readTermCount() throws FileNotFoundException, IOException {
		BufferedReader top_file = new BufferedReader(new FileReader(termCount));
		Topic topic = null;
		int i = 0;
		for (String tmp = top_file.readLine(); tmp != null; tmp = top_file.readLine()) {
			if (tmp.startsWith("Topic#")) {
				String topicid = tmp.replaceFirst("Topic#", "");
				topic = this.getTopic(topicid);
				i = 0;
			} else if (tmp.startsWith("passage")) {
				if (topic != null) {
					List psgs = topic.getPassages();

					Passage psg = (Passage) psgs.get(i);
					Pattern p = Pattern.compile("passage .* terms was found >> ");
					Matcher m = p.matcher(tmp);
					StringBuffer sb = new StringBuffer();
					if (m.find()) {
						m.appendReplacement(sb, "");
						m.appendTail(sb);
					} else {
						Pattern p2 = Pattern.compile("passage .* terms was found\\.");
						Matcher m2 = p2.matcher(tmp);
						if (m2.find()) {
							sb = new StringBuffer();
						}
					}
					psg.setTerms(sb.toString());
					psg.setFoundTerms(sb.toString().split(" "));
					i++;
				}
			}
		}
		top_file.close();
		return top_file;
	}
*/
	/**
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	private void initConfig() {
		try {
			BufferedReader conf_file = new BufferedReader(new FileReader("config/all_combine_config.properties"));
			for (String tmp = conf_file.readLine(); tmp != null; tmp = conf_file.readLine()) {
				String[] conf = tmp.split("=");
				if (conf[0].equals("formatted-combined-txt-2-name")) {
					this.termFormatted2 = conf[1];
				} else if (conf[0].equals("formatted-combined-txt-1-name")) {
					this.termFormatted = conf[1];
				} else if (conf[0].equals("formatted-combined-txt-name")) {
					this.termFormatted = conf[1];
				} else if (conf[0].equals("top-passages-name")) {
					this.topPassages = conf[1];
				} else if (conf[0].equals("cluster-size")) {
					this.phases = conf[1].split(",");
				} else if (conf[0].equals("output-line")) {
					this.outputLine = new Integer(conf[1]).intValue();
				} else if (conf[0].equals("output-file-name")) {
					this.outputFile = conf[1];
				}
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NumberFormatException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	class PassageComp implements Comparator<Passage>{

		public int compare(Passage o1, Passage o2) {
			return o2.getWeight().compareTo(o1.getWeight());
		}

	}

	public static void main(String[] args) {
		AllCombine ac = new AllCombine();

	}
}
